﻿/**************************************************************
 *
 * 唯一标识：4bf11f26-2ca8-4628-ab98-813e1a5572e6
 * 命名空间：Sgr.EntityFrameworkCore.EntityConfigurations
 * 创建时间：2023/8/4 9:21:40
 * 机器名称：DESKTOP-S0D075D
 * 创建者：antho
 * 电子邮箱：fengqinhua2016@163.com
 * 描述：
 *
 **************************************************************/

using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Metadata.Builders;
using Sgr.Data;
using Sgr.Domain.Entities;
using Sgr.Domain.Entities.Auditing;

namespace Sgr.EntityFrameworkCore.EntityConfigurations
{
    public abstract class EntityTypeConfigurationBase<TEntity, TPrimaryKey>
         : IEntityTypeConfiguration<TEntity> where TEntity : class, IEntity<TPrimaryKey>
    {
        public EntityTypeConfigurationBase(DatabaseType? databaseType)
        {
            this.DatabaseType = databaseType;
        }

        /// <summary>
        /// 数据库类型
        /// </summary>
        public DatabaseType? DatabaseType { get; private set; }

        /// <summary>
        /// 是否尝试使用数据库原生/推荐 JSON 类型（可被子类覆写）
        /// </summary>
        protected virtual bool UseJsonColumnForExtensionData => true;

        /// <summary>
        /// 获取数据库字段名称大小写设置规则
        /// </summary>
        /// <returns></returns>
        protected virtual ColumnNameCase GetColumnNameCase()
        {
            return ColumnNameCase.Lowercase;
        }

        protected virtual void AddTreeNodeIndex(EntityTypeBuilder<TEntity> builder, bool withNodePath = false)
        {
            var entityType = typeof(TEntity);

            if (typeof(ITreeNode<TPrimaryKey>).IsAssignableFrom(entityType))
            {
                builder.HasIndex("ParentId");

                if (withNodePath)
                    builder.HasIndex("NodePath");
            }
        }

        protected virtual void AddOrgIndex(EntityTypeBuilder<TEntity> builder)
        {
            var entityType = typeof(TEntity);

            if (IsAssignableToOpenGenericType(entityType, typeof(IMustHaveOrg<>)))
            {
                builder.HasIndex("OrgId");
            }
        }

        /// <summary>
        ///
        /// </summary>
        /// <param name="builder"></param>
        /// <exception cref="NotImplementedException"></exception>
        public virtual void Configure(EntityTypeBuilder<TEntity> builder)
        {
            var entityType = typeof(TEntity);

            builder.HasKey(b => b.Id);

            //builder.PropertyAndHasColumnName("Id", GetColumnNameCase(), "sgr")
            //    .ValueGeneratedNever()
            //    .HasComment("主键");

            if (typeof(IAggregateRoot).IsAssignableFrom(entityType))
                builder.PropertyAndHasColumnName("Id", GetColumnNameCase(), "sgr")
                    .ValueGeneratedNever()
                    .HasComment("主键");
            else
                builder.PropertyAndHasColumnName("Id", GetColumnNameCase(), "sgr")
                    .HasComment("主键");

            if (typeof(ITreeNode<TPrimaryKey>).IsAssignableFrom(entityType))
            {
                builder.PropertyAndHasColumnName("ParentId", GetColumnNameCase(), "sgr")
                    .IsRequired()
                    .HasComment("上级节点Id");

                builder.PropertyAndHasColumnName("NodePath", GetColumnNameCase(), "sgr")
                    .IsRequired()
                    .HasMaxLength(255)
                    .HasComment("树节点层次目录");
            }

            if (typeof(IExtendableObject).IsAssignableFrom(entityType))
            {
                ConfigureExtensionData(builder);
            }

            if (typeof(IOptimisticLock).IsAssignableFrom(entityType))
            {
                builder.PropertyAndHasColumnName("RowVersion", GetColumnNameCase(), "sgr")
                    .IsConcurrencyToken()
                    .IsRequired()
                    //.HasValueGenerator<LongRowVersionValueGenerator>()
                    //.ValueGeneratedOnAddOrUpdate()
                    //.IsRowVersion()
                    .HasComment("行版本");
            }

            if (typeof(IDomainEventManage).IsAssignableFrom(entityType))
            {
                builder.Ignore("DomainEvents");
            }

            if (typeof(ISoftDelete).IsAssignableFrom(entityType))
            {
                builder.PropertyAndHasColumnName("IsDeleted", GetColumnNameCase(), "sgr")
                    .IsRequired()
                    .HasComment("是否已经被软删除");
            }

            if (IsAssignableToOpenGenericType(entityType, typeof(IMustHaveOrg<>)))
            {
                builder.PropertyAndHasColumnName("OrgId", GetColumnNameCase(), "sgr")
                    .IsRequired()
                    .HasComment("所在组织ID");
            }

            if (IsAssignableToOpenGenericType(entityType, typeof(ICreationAudited<>)))
            {
                builder.PropertyAndHasColumnName("CreatorUserId", GetColumnNameCase(), "sgr")
                    .HasComment("创建的用户ID");

                builder.PropertyAndHasColumnName("CreationTime", GetColumnNameCase(), "sgr")
                    .HasComment("创建时间");
            }

            if (IsAssignableToOpenGenericType(entityType, typeof(ICreationAndModifyAudited<>)))
            {
                builder.PropertyAndHasColumnName("LastModifierUserId", GetColumnNameCase(), "sgr")
                    .HasComment("修改的用户ID");

                builder.PropertyAndHasColumnName("LastModificationTime", GetColumnNameCase(), "sgr")
                    .HasComment("修改时间");
            }

            if (IsAssignableToOpenGenericType(entityType, typeof(IFullAudited<>)))
            {
                builder.PropertyAndHasColumnName("DeleterUserId", GetColumnNameCase(), "sgr")
                    .HasComment("删除的用户ID");

                builder.PropertyAndHasColumnName("DeletionTime", GetColumnNameCase(), "sgr")
                    .HasComment("删除时间");
            }
        }

        /// <summary>
        /// 统一配置 ExtensionData 列（JSON / 文本）
        /// 提供可复用的 providerName 解析和列类型解析逻辑。
        /// 子类若需为其它 JSON 扩展属性复用，可调用:
        ///   ConfigureJsonColumn(otherPropBuilder, provider, fallbackLength);
        /// </summary>
        protected virtual void ConfigureExtensionData(EntityTypeBuilder<TEntity> builder)
        {
            var prop = builder.PropertyAndHasColumnName("ExtensionData", GetColumnNameCase(), "sgr")
                              .HasComment("扩展对象/实体(JSON)");

            ConfigureJsonColumn(prop, Constant.ExtendableObjectMaxLength);
        }

        ///// <summary>
        ///// 获取当前上下文的数据库 provider 名称（统一为小写），无法获取则返回空字符串。
        ///// </summary>
        //protected virtual string GetCurrentProviderName(EntityTypeBuilder<TEntity> builder)
        //{
        //    // 与原实现保持兼容，安全获取 Annotation
        //    var annotation = builder.Metadata.Model.FindAnnotation("Relational:ProviderName");

        //    var raw = annotation?.Value?.ToString() ?? string.Empty;
        //    return raw.ToLowerInvariant();
        //}

        /// <summary>
        /// 根据 provider 名称返回适配的 JSON 列类型；无法识别返回 null。
        /// 子类可覆写，自定义新增/替换策略。
        /// </summary>
        protected virtual string? GetJsonColumnType()
        {
            return this.DatabaseType! switch
            {
                Data.DatabaseType.MySql => "json",
                Data.DatabaseType.PostgreSql => "jsonb",
                Data.DatabaseType.SqlServer => "nvarchar(max)", // SQL Server 通常用 nvarchar(max) 存 JSON
                Data.DatabaseType.SQLite => "TEXT",
                Data.DatabaseType.Oracle => "CLOB",
                Data.DatabaseType.DB2 => "CLOB",
                _ => null // 未支持的数据库类型返回 null，或可抛出异常
            };
        }

        /// <summary>
        /// 统一执行 “优先 JSON 类型，回退为限定长度字符串” 的列配置逻辑。
        /// 可被子类直接复用在其它扩展属性。
        /// </summary>
        /// <param name="prop">属性构建器</param>
        /// <param name="fallbackLength">回退字符串的最大长度</param>
        protected virtual void ConfigureJsonColumn(PropertyBuilder prop, int fallbackLength)
        {
            if (UseJsonColumnForExtensionData)
            {
                var jsonType = GetJsonColumnType();
                if (jsonType != null)
                {
                    prop.HasColumnType(jsonType);
                    return;
                }
            }

            // 回退
            prop.HasMaxLength(fallbackLength);
        }

        private static bool IsAssignableToOpenGenericType(Type givenType, Type genericType)
        {
            return Array.Exists(givenType.GetInterfaces(), t => t.IsGenericType && t.GetGenericTypeDefinition() == genericType);

            //var interfaceTypes = givenType.GetInterfaces();
            //foreach (var it in interfaceTypes)
            //{
            //    if (it.IsGenericType && it.GetGenericTypeDefinition() == genericType)
            //        return true;
            //}

            //if (givenType.IsGenericType && givenType.GetGenericTypeDefinition() == genericType)
            //    return true;

            //Type? baseType = givenType.BaseType;
            //if (baseType == null) return false;

            //return IsAssignableToGenericType(baseType, genericType);
        }
    }
}